package io.renren.iots.socket.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.renren.modules.iotp.entity.IOTPBean;
import io.renren.modules.iotp.utils.Code;
import io.renren.modules.iotp.utils.IotpBodyUtils;
import io.renren.modules.iotp.utils.Msg_rellect;  

/**
 * @ClassName: NIOSServer
 * @Description: NIO通讯服务端
 * @author 周西栋
 * @date 2018年5月10日
 *
 */
public class NIOSServer {
	/**
	 * 日志
	 */
	private Logger logger = LoggerFactory.getLogger(getClass());
	/**
	 * 协议解析工具类
	 */
	private static IotpBodyUtils ibu = new IotpBodyUtils();
	/**
	 * 返回通讯的结果（返回值）
	 */
	private byte[] iotp_result = null;
	
	/**
	 * 测试计数
	 */
	private int index = 0;
			

    private int port = 8888;
    /*接受数据缓冲区*/
    private ByteBuffer rBuffer = ByteBuffer.allocate(8354);
    /*映射客户端channel */
    private Selector selector;
    
    /**
     * 数据拼接工具类
     */
    private Msg_rellect mr = new Msg_rellect();
    
    /**
     * 容器list
     */
    private List<byte[]> byte_list = new ArrayList<>();
    
    /* 测试用，用完删除 */
    int readsize = 0;
    
    public NIOSServer(){
        try {
            init();
            listen();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private void init() throws IOException{
        /* 
         *启动服务器端，配置为非阻塞，绑定端口，注册accept事件 
         *ACCEPT事件：当服务端收到客户端连接请求时，触发该事件 
         */  
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();  
        serverSocketChannel.configureBlocking(false);  
        ServerSocket serverSocket = serverSocketChannel.socket();  
        serverSocket.bind(new InetSocketAddress(port));  
        selector = Selector.open();  
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);  
        System.out.println("server start on port:"+port);  
    }

    /** 
     * 服务器端轮询监听，select方法会一直阻塞直到有相关事件发生或超时 
     */  
    private void listen(){

        while (true) {
            try {
                selector.select();//返回值为本次触发的事件数  
                Set<SelectionKey> selectionKeys = selector.selectedKeys();  
                for(SelectionKey key : selectionKeys){
                    handle(key);
                }
                selectionKeys.clear();//清除处理过的事件 
            } catch (Exception e) {
                e.printStackTrace();  
                continue;  
            }  
        }
    }

    /**
     * 处理不同的事件 
     */ 
    private void handle(SelectionKey selectionKey) throws IOException { 

        ServerSocketChannel server = null;
        SocketChannel client = null;
        int count=0;
        if (selectionKey.isAcceptable()) {
            /* 
             * 客户端请求连接事件 
             * serversocket为该客户端建立socket连接，将此socket注册READ事件，监听客户端输入 
             * READ事件：当客户端发来数据，并已被服务器控制线程正确读取时，触发该事件 
             */  
            server = (ServerSocketChannel) selectionKey.channel();
            client = server.accept();
            client.configureBlocking(false);
            client.register(selector, SelectionKey.OP_READ);
        } else if (selectionKey.isReadable()) {
            /*
             * READ事件，收到客户端发送数据，读取数据后继续注册监听客户端 
             */
            client = (SocketChannel) selectionKey.channel();
            try {
				count = client.read(rBuffer);
				readsize += count;
				if (count > 0) {
					System.out.println("我读到的长度为："+count);
					rBuffer.flip();
					byte[] data = ibu.getNewByteArray(rBuffer.array(), 0, count);
					System.out.println("我读到的data长度为："+data.length);
					System.out.println("我读到的总长度为："+readsize);
	                iotp_result = null;
	                byte_list = mr.rellect(data); 
	                index += byte_list == null ? 0 : byte_list.size();
	                System.out.println("------ --------- index:"+index);
	                if(byte_list != null && byte_list.size() > 0){
	                	for(byte[] b : byte_list){
	                		IOTPBean iotp = ibu.toIotpBean(b);
	                		Code code = ibu.getIotp_code().get(iotp);
	                		if(iotp != null && iotp.getBody() != null){
	                			index = 0;
	                			logger.info("从客户端【 "+client.getRemoteAddress()+" 】收到的信息转成可读内容 ");
	                			iotp_result = ibu.getResult(iotp, code);
	                			// 将返回值发给客户端
//	                			client.write(ByteBuffer.wrap(iotp_result));
	                			sendMsg(client,iotp_result);
	                			//将转成的iotp再发回客户端
//	                			for(byte[] b1 :ibu.toByteArray(iotp)){
//	                				client.write(ByteBuffer.wrap(b1));
//	                			}
	                			logger.info("向客户端【 "+client.getRemoteAddress()+" 】发送了发来的数据"+iotp.toString().length());
	                			
	                			// 将信息存入配置或者转发
//	                			sendKafka(iotp);
	                		}
	                	}
	                }
	            }  
            } catch (Exception e1) {
				e1.printStackTrace();
			} finally {
				rBuffer.clear();
			}
        }   
    }  
    
    /**
	 * @throws InterruptedException 
	 * @throws IOException 
	 * @author 周西栋
	 * @date 2018年6月26日
	 * @Title: sendMsg
	 * @Description: 发送字节数组
	 * @param @param socket
	 * @param @param msg    参数
	 * @return void    返回类型
	 * @throws
	 */
	public void sendMsg(SocketChannel socket,byte[] msg) throws IOException, InterruptedException{
		int p = 0;
		while(p < msg.length){
			int sendSize = socket.write(ByteBuffer.wrap(msg,p,msg.length-p));
			p += sendSize;
			if(p != msg.length && sendSize > 0){
				System.out.println("我没有发完，只发了 "+ p +" 个");
				Thread.sleep(1);
			}else if(sendSize > 0){
				System.out.println("我发完了 "+ p +" 个");
				index += p;
				System.out.println("我累计发送了 "+ index +" 个");
			}
		}
	}
    
    /**
     * @author 周西栋
     * @date 2018年6月14日
     * @Title: main
     * @Description: main方法
     * @param @param args
     * @param @throws IOException    参数
     * @return void    返回类型
     * @throws
     */
    public static void main(String[] args) throws IOException {

        new NIOSServer();
    }  
}